package com.spring_mysql_redis.webSocket;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

import cn.hutool.core.util.IdUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.spring_mysql_redis.controller.CommentController;
import com.spring_mysql_redis.entity.Friend;
import com.spring_mysql_redis.entity.Message;
import com.spring_mysql_redis.mapper.FriendMapper;
import com.spring_mysql_redis.mapper.MessageMapper;
import com.spring_mysql_redis.utils.MsgSenderFanout;
import com.spring_mysql_redis.utils.RedisUtil;
import com.spring_mysql_redis.utils.TestUtils;
import com.spring_mysql_redis.utils.TimeUtitl;
import org.apache.shiro.subject.ExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.PropertySource;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.RestController;

/**
 * WebSocket
 *
 * @date 2022-10-02
 * @version 1.0.0
 */
@PropertySource(value = "classpath:application.yml")
@ServerEndpoint("/WebSocket/{id}")
@RestController
public class WebSocketServer  {

    @Value("${server.port}")
    private Integer port;

    //静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;

    private Logger logger = LoggerFactory.getLogger(WebSocketServer.class);

    // 存储会话
    private static ConcurrentHashMap<String, WebSocketServer> webSocket = new ConcurrentHashMap<String, WebSocketServer>();

    private static RedisUtil redisUtil;

    private static FriendMapper friendMapper;
    private static MessageMapper messageMapper;
    private static MsgSenderFanout msgSenderFanout;

    private static TestUtils testUtils;

    private String userId;
    private Session session;

    public static void setApplicationContext(ConfigurableApplicationContext applicationContext) {
        WebSocketServer.redisUtil = applicationContext.getBean(RedisUtil.class);
        WebSocketServer.friendMapper = applicationContext.getBean(FriendMapper.class);
        WebSocketServer.messageMapper = applicationContext.getBean(MessageMapper.class);
        WebSocketServer.msgSenderFanout = applicationContext.getBean(MsgSenderFanout.class);
        WebSocketServer.testUtils = applicationContext.getBean(TestUtils.class);
    }

    /**
     * 接入连接回调
     *
     * @param session 会话对象
     * @param id      会话ID
     * @throws Exception 异常
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("id") String id) throws Exception {
        this.userId = id;
        this.session = session;
        webSocket.put(id, this);
        //ws在线用户存入redis
        redisUtil.set("ws:" + id, "10");
        System.out.println(id + "接入连接");
    }

    /**
     * 关闭连接回调
     */
    @OnClose
    public void onClose() {
        //关闭连接时把ws从redis移除
        redisUtil.del("ws:" + this.userId, "10");
        webSocket.remove(this.userId);
        System.out.println(this.userId + "关闭连接");
    }

    /**
     * 收到客户端发来消息回调
     *
     * @param message
     */
    @OnMessage
    public void onMessage(String message) throws IOException {
        //测试大量数据同时发送
//        for (int i=0;i<101; i++){
//            testUtils.asyncList1(message);
//        }
        Message msg = JSON.parseObject(message, Message.class);
        //心跳
        if("2000".equals(msg.getType())){
            sendMessage(msg.getToId(),msg);
        }else {
            msgSenderFanout.send(message);
        }
    }

    /**
     * 会话出现错误回调
     *
     * @param error   错误信息
     */
    @OnError
    public void onError(Throwable error) {

    }

    /**
     * 发送消息(心跳)给客户端
     *
     * @param message 消息
     * @throws IOException 异常
     */
    public void sendMessage(String toId, Message message) {
        WebSocketServer webSocketServer =  webSocket.get(toId);
        webSocketServer.session.getAsyncRemote().sendText(JSON.toJSONString(message));
    }

    /**
     * 给指定的会话发送消息
     *
     * @param toId      会话ID
     * @param message 消息
     * @throws IOException 异常
     */
    public void sendMessageToId(String toId, Message message) {
        WebSocketServer webSocketServer =  webSocket.get(toId);
        //因为在redis下已经判断用户是否在线，在本服务下ws不在线，直接把消息丢弃
        if(webSocketServer == null){

        }else {
            //连接在线
            message.setState(10);
            messageMapper.insert(message);
            webSocketServer.session.getAsyncRemote().sendText(JSON.toJSONString(message));
        }
    }

    /**
     * 群发消息
     *
     * @param message 消息
     * @throws IOException 异常
     */
    public void sendMessageToAll(String toId,Message message) {
//        for (String key : webSocket.keySet()) {
//            webSocket.get(key).sendMessage(toId,message);
//        }
    }

    private static Message robot(Message message) {
        System.out.println("成功--------------------------robot(ImMessage imMessage)");
        String response = HttpUtil.get("http://api.qingyunke.com/api.php?key=free&appid=0&msg=" + message.getContent());
        JSONObject jsonObject = JSON.parseObject(response);
        String content = (String) jsonObject.get("content");
        Message robotImMessage = message;
        robotImMessage.setFromId("10000");
        robotImMessage.setFromName("图灵菲菲");
        robotImMessage.setFromIcon("");
        robotImMessage.setContent(content);
        message.setSendTime(TimeUtitl.dateTime());
        return message;
    }

    //关注用户方法
    public void addUserMessage(Message msg) {
        if(msg.getType().equals(Message.TYPE_ADD)){
            Friend friend = new Friend();
            friend.setUserUuId(msg.getFromId());
            friend.setFriendUuId(msg.getToId());
            friend.setCreateTime(TimeUtitl.dateTime());
            try {
                //唯一索引，重复报异常
                Integer insert = friendMapper.insert(friend);
                if (insert > 0){
                    //查询判断对方是否已关注自己
                    Friend selectOne = friendMapper.selectOne(new QueryWrapper<Friend>().eq("user_uu_id", msg.getToId()).eq("friend_uu_id", msg.getFromId()));
                    if(selectOne == null){
                        sendMessageToId(msg.getToId(), msg);
                    }else {
                        msg.setType(Message.TYPE_ADD_ACCEPTED);
                        sendMessageToId(msg.getToId(), msg);
                    }
                }
            }catch (Exception e){

            }
        }
        //发送回关请求
        if(msg.getType().equals(Message.TYPE_ADD_ACCEPT)){
            //创建好友对象
            Friend friend = new Friend();
            friend.setUserUuId(msg.getFromId());
            friend.setFriendUuId(msg.getToId());
            friend.setCreateTime(TimeUtitl.dateTime());
            try {
                Integer insert = friendMapper.insert(friend);
                if (insert > 0){
                    msg.setType(Message.TYPE_ADD);
                    sendMessageToId(msg.getToId(), msg);
                }
            }catch (Exception e){

            }
        }
    }
    //关注用户不在线方法，20代表不在线状态
    public void addUserMessage20(Message msg) {
        if(msg.getType().equals(Message.TYPE_ADD) || msg.getType().equals(Message.TYPE_ADD_ACCEPT)){
            Friend friend = new Friend();
            friend.setUserUuId(msg.getFromId());
            friend.setFriendUuId(msg.getToId());
            friend.setCreateTime(TimeUtitl.dateTime());
            try {
                //唯一索引，重复报异常
                Integer insert = friendMapper.insert(friend);
                if (insert > 0){
                    //查询判断对方是否已关注自己
                    Friend selectOne = friendMapper.selectOne(new QueryWrapper<Friend>().eq("user_uu_id", msg.getToId()).eq("friend_uu_id", msg.getFromId()));
                    if(selectOne == null){
                        messageMapper.insert(msg);
                    }else {
                        msg.setType(Message.TYPE_ADD_ACCEPTED);
                        messageMapper.insert(msg);
                    }
                }
            }catch (Exception e){

            }
        }
    }
    //私聊群聊方法
    public void chatMessage(Message msg) {
        if (msg.getType().equals(Message.TYPE_ONE)){
//            if (msg.getToId().equals("1") && !(msg.getContent().startsWith("[IMG]") || msg.getContent().startsWith("[FILE]"))) { // 机器人回复
//                Message robot = WebSocketServer.robot(msg);
//                Message robotImMessage = new Message();
//                BeanUtils.copyProperties(robot, robotImMessage);
//                sendMessageToId(msg.getFromId(), msg);
//            }
            sendMessageToId(msg.getToId(), msg);
        }
    }
    /**
     * rabbit订阅队列中的消息(发送消息)
     *
     * @param message
     */
    @RabbitListener(queues = "${spring.rabbitmq.modules.queue.name}")
    @RabbitHandler
    public void process(String message) throws IOException {
        System.out.println("----------------------"+message);
        Message msg = JSON.parseObject(message, Message.class);
        msg.setId(String.valueOf(UUID.randomUUID()).replace("-",""));
        msg.setSendTime(TimeUtitl.dateTime());
        //集群需要，redis中用户在线
        if(redisUtil.get("ws:"+msg.getToId()) != null){
            //关注方法
            addUserMessage(msg);
            //私聊群聊方法
            chatMessage(msg);
        }else {
            //只让8080服务存未读，避免多存
            if(port == 8080){
                msg.setState(20);
                addUserMessage20(msg);
                messageMapper.insert(msg);
            }
        }

    }

}

